package myCobra

import (
	"fmt"
	"os"
	"strings"

	flag "github.com/spf13/pflag"
)

type Command struct {
	// Use is the one-line usage message.
	Use string

	// Short is the short description shown in the 'help' output.
	Short string

	// Long is the long message shown in the 'help <this-command>' output.
	Long string

	// Run: Typically the actual work function. Most commands will only implement this.
	Run func(cmd *Command, args []string)

	// commands is the list of sub-commands supported by this command.
	commands []*Command

	// the subcommand to execute
	subcmd *Command 

	// the parnet command
	parentcmd *Command

	// validArgs is list of all valid arguments of this command that are accepted in bash completions
	validArgs []string

	// flags parse flags for command.
	flags *flag.FlagSet

	// print the help information
	help bool
}

// Execute uses the args (os.Args[1:] by default)
// and run through the command tree finding appropriate matches
// for commands and then corresponding flags.
func (c *Command) Execute() error {
	if  c == nil {
		return fmt.Errorf("Called Execute() on a nil Command")
	}
	c.execute()
	return nil
}

func (c *Command) execute() {
	// c is root command
	if  c.parentcmd == nil { 
		c.Traverse(os.Args[1:])
	}

	if c.subcmd == nil {
		if c.help {
			c.Help()
			return 
		}
		c.Run(c, c.validArgs)
		return
	}
	c.subcmd.execute()
}

// add Command cmd to Command c
// the cmd can't be c itself
func (c *Command) AddCommand(cmd *Command) {
	if cmd == c {
		panic("Command can't be a child of itself")
	}

	// the command has been added
	for _, v := range c.commands {
		if v == cmd {
			return
		}
	}

	c.commands = append(c.commands, cmd)
	cmd.parentcmd = c
}

// Traverse the command tree to find the command, and parse args for
// each parent.
func (c *Command) Traverse(args []string) {
	// no args 
	if len(args) < 1 {
		return
	}
	// args exists, find its sub-command or its args
	for _, v := range c.commands {
		if v.Use == args[0] { 
			// have sub-command
			c.validArgs = nil
			c.subcmd = v
			v.Traverse(args[1:])
			return
		}
	}
	// no sub-command exists
	c.Flags().BoolVarP(&c.help, "help", "h", false, "print the help information")
	c.Flags().Parse(args)
	c.validArgs = c.Flags().Args()
}

func (c *Command) Flags() *flag.FlagSet {
	if c.flags == nil {
		c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
	}
	return c.flags
}

// Name returns the command's name: the first word in the use line.
func (c *Command) Name() string {
	name := c.Use
	i := strings.Index(name, " ")
	if i >= 0 {
		name = name[:i]
	}
	return name
}

// Help print the help information for "-h"
func (c *Command) Help(){
	c.print_usage();
}

func (c *Command) print_usage() {
	fmt.Printf("%s\n", c.Long)
	if (len(c.commands) > 0) {
		fmt.Printf("Usage:\n")
		fmt.Printf("\t%s [command]\n\n", c.Name())
		fmt.Printf("Available commands:\n")
		for _, v := range c.commands {
			fmt.Printf("\t%-8s:%s\n", v.Name(), v.Short)
		}
	}
	fmt.Println()
}